package it.uniroma2.orchestrator.workflow;

import it.uniroma2.orchestrator.Servizio;

import java.util.Vector;

public class Workflow {

	public Vector<Nodo> workflow;
	private Vector<Servizio> serviziTotali;
	private Vector<Servizio> serviziRegole;

	private Vector<Vector> workflowOrdinati;
	private int workflowOrdineIndex = 0;

	public Workflow(Vector<Servizio> serviziTotali,
			Vector<Servizio> serviziRegole) {
		this.serviziRegole = serviziRegole;
		this.serviziTotali = serviziTotali;
		this.workflow = new Vector<Nodo>();
		this.workflowOrdinati = new Vector<Vector>();
	}

	public void creaWorkflow() {
		workflow.add(new Nodo(serviziRegole.firstElement(), false));

		for (int i = 0; i < workflow.size(); i++) {
			Nodo nodoTmp = workflow.elementAt(i);
			if (!nodoTmp.isDummy()) {
				if (nodoTmp.hasPreCondizione()) {
					Vector<Servizio> vettoreServTmp = nodoTmp.getServizio()
							.getPreCondizione();
					for (int j = 0; j < vettoreServTmp.size(); j++) {
						Servizio servizioTmp = vettoreServTmp.elementAt(j);
						if (servizioTmp.getOutput().compareTo(
								nodoTmp.getServizio().getInput()) == 0) {
							Nodo nodoTmpPre = this.aggiungiServizio(
									servizioTmp, false);
							nodoTmp.aggiungiNodoPrecedente(nodoTmpPre);
							nodoTmpPre.aggiungiNodoSuccessivo(nodoTmp);

						} else {
							Servizio servTmp = new Servizio(
									servizioTmp.getOutput() + "to"
											+ nodoTmp.getServizio().getInput());
							servTmp.setInput(servizioTmp.getOutput());
							servTmp.setOutput(nodoTmp.getServizio().getInput());
							Nodo nodoTmpDummy = aggiungiServizio(servTmp, true);
							Nodo nodoTmpPre = aggiungiServizio(servizioTmp,
									false);
							nodoTmp.aggiungiNodoPrecedente(nodoTmpDummy);
							nodoTmpDummy.aggiungiNodoPrecedente(nodoTmpPre);
							nodoTmpDummy.aggiungiNodoSuccessivo(nodoTmp);
							nodoTmpPre.aggiungiNodoSuccessivo(nodoTmpDummy);
						}
					}
				} else if (!nodoTmp.hasPreCondizione()) {

				}
				if (nodoTmp.hasPostCondizione()) {
					Vector<Servizio> vettoreServTmp = nodoTmp.getServizio()
							.getPostCondizione();
					for (int j = 0; j < vettoreServTmp.size(); j++) {
						Servizio servizioTmp = vettoreServTmp.elementAt(j);
						if (servizioTmp.getInput().compareTo(
								nodoTmp.getServizio().getOutput()) == 0) {
							Nodo nodoTmpPost = this.aggiungiServizio(
									servizioTmp, false);
							nodoTmp.aggiungiNodoSuccessivo(nodoTmpPost);
							nodoTmpPost.aggiungiNodoPrecedente(nodoTmp);

						} else {
							Servizio servTmp = new Servizio(nodoTmp
									.getServizio().getOutput()
									+ "to"
									+ servizioTmp.getInput());
							servTmp.setInput(nodoTmp.getServizio().getOutput());
							servTmp.setOutput(servizioTmp.getInput());
							Nodo nodoTmpDummy = this.aggiungiServizio(servTmp,
									true);
							Nodo nodoTmpPost = aggiungiServizio(servizioTmp,
									false);
							nodoTmp.aggiungiNodoSuccessivo(nodoTmpDummy);
							nodoTmpDummy.aggiungiNodoSuccessivo(nodoTmpPost);
							nodoTmpDummy.aggiungiNodoPrecedente(nodoTmp);
							nodoTmpPost.aggiungiNodoPrecedente(nodoTmpDummy);

						}
					}
				} else if (!nodoTmp.hasPostCondizione()) {

				}
			}
		}
	}

	public Nodo aggiungiServizio(Servizio servizio, boolean dummy) {
		for (int i = 0; i < workflow.size(); i++) {
			if (this.workflow.elementAt(i).getServizio().getNome()
					.compareTo(servizio.getNome()) == 0) {
				return workflow.elementAt(i);
			}
		}
		workflow.add(new Nodo(servizio, dummy));
		return workflow.lastElement();
	}

	public void ordinaWorkflow() {
		boolean stop = false;
		Nodo nodoIniziale = null;
		Vector<Vector> v = new Vector<Vector>();
		int indexVector = 0;
		int posVector = 0;

		for (int i = 0; i < workflow.size(); i++) {
			if (workflow.elementAt(i).isEmptyPre()) {
				nodoIniziale = workflow.elementAt(i);
				break;
			}

		}
		Nodo nodoTmp = nodoIniziale;
		v.add(new Vector<Integer>());
		v.elementAt(indexVector).add(0);
		while (!nodoTmp.isEmptyPost()) {
			nodoTmp = nodoTmp.nodoSucc(0);
			v.elementAt(indexVector).add(0);
			posVector++;
		}

		while (!stop) {
			if (nodoTmp.numSucc() > (Integer) ((v.elementAt(indexVector))
					.elementAt(posVector)) + 1 && nodoTmp.numSucc() >= 2) {
				v.add(new Vector<Integer>());
				for (int i = 0; i < posVector; i++) {
					v.elementAt(indexVector + 1).add(
							v.elementAt(indexVector).elementAt(i));
				}
				v.elementAt(indexVector + 1).add(
						(Integer) (v.elementAt(indexVector)
								.elementAt(posVector)) + 1);
				indexVector++;
				nodoTmp = nodoTmp.nodoSucc((Integer) (v.elementAt(indexVector)
						.elementAt(posVector)));

				while (!nodoTmp.isEmptyPost()) {
					nodoTmp = nodoTmp.nodoSucc(0);
					v.elementAt(indexVector).add(0);
					posVector++;
				}
				v.elementAt(indexVector).add(0);
				posVector++;

			} else {
				if (nodoTmp.equals(nodoIniziale))
					stop = true;
				else {
					nodoTmp = nodoTmp.nodoPrec();
					posVector--;
				}
			}

		}

		for (int i = 0; i < v.size(); i++) {
			Vector<Nodo> workflowTmp = new Vector<Nodo>();
			workflowTmp.add(nodoIniziale);
			Nodo nodoTmpWork = nodoIniziale;
			for (int j = 0; j < v.elementAt(i).size() - 1; j++) {
				nodoTmpWork = nodoTmpWork.nodoSucc((Integer) v.elementAt(i)
						.elementAt(j));
				workflowTmp.add(nodoTmpWork);
			}
			this.workflowOrdinati.add(workflowTmp);

		}

	}

	public Vector<Nodo> getWorkflow() {
		return this.workflowOrdinati.elementAt(this.workflowOrdineIndex);
	}

	public void prossimoWorkflow() {
		this.workflowOrdineIndex++;
		this.workflowOrdineIndex = this.workflowOrdineIndex
				% this.workflowOrdinati.size();
	}

}
